Skip to content

Hotfix: restore BoundsForRange gate, raise focus poll to 80ms#255

Merged
FuJacob merged 1 commit into
mainfrom
hotfix/chrome-ax-hang-bounds-for-range-gate
May 25, 2026
Merged

Hotfix: restore BoundsForRange gate, raise focus poll to 80ms#255
FuJacob merged 1 commit into
mainfrom
hotfix/chrome-ax-hang-bounds-for-range-gate

Conversation

@FuJacob
Copy link
Copy Markdown
Owner

@FuJacob FuJacob commented May 25, 2026

Summary

PR #218 made the BoundsForRange AX call unconditional, including inside the deep-tree walker that visits many AXStaticText leaves per focus poll. BoundsForRange is a synchronous cross-process call into the focused app's AX implementation — in Chrome that's a round-trip into the renderer, and calling it on nodes that don't advertise support stalled the main thread badly enough to freeze typing (users reported the menu bar showing the loading indicator while typing in Chrome text fields). This restores the supportsBoundsForRange gate at the three call sites while keeping #218's rectIsNearAnchor validator for elements that do advertise support. Also bumps the focus poll interval 50ms → 80ms to reduce baseline AX pressure on the main thread.

Validation

xcodebuild -project Cotabby.xcodeproj -scheme Cotabby -destination 'platform=macOS' build
# ** BUILD SUCCEEDED **

xcodebuild -project Cotabby.xcodeproj -scheme Cotabby -destination 'platform=macOS' build-for-testing
# ** TEST BUILD SUCCEEDED **

swiftlint lint --quiet Cotabby/Services/Focus/AXTextGeometryResolver.swift \
  Cotabby/Services/Focus/FocusSnapshotResolver.swift \
  Cotabby/Services/Focus/FocusTracker.swift \
  Cotabby/Models/SuggestionModels.swift \
  Cotabby/Models/SuggestionSettingsModel.swift \
  CotabbyTests/AXTextGeometryResolverTests.swift
# exit 0

test-without-building fails locally with a Team ID / code-signing mismatch on the host app vs. test bundle (code signature ... not valid for use in process: mapping process and mapped file (non-platform) have different Team IDs) — same condition called out in CLAUDE.md. Tests need to be run in CI or with a configured signing identity to verify end-to-end. The AXTextGeometryResolverTests were updated to pass supportsBoundsForRange: true for the restored parameter; the rectIsNearAnchor unit tests are unchanged and still cover the anchor accept/reject boundary.

Manual smoke test pending — should be verified by typing in a Chrome text field on a page with a large AX tree (Gmail, Google Docs, GitHub PR review) before promoting.

Linked issues

Refs #218

Risk / rollout notes

  • Behavior change: caret geometry for AX elements that support BoundsForRange without advertising it in parameterizedAttributeNames (a minority of Electron/WebKit cases referenced in Try BoundsForRange optimistically for exact caret placement #218's commit message) will fall back to TextMarker → child text runs → AXFrame instead of optimistic BoundsForRange. This is the pre-Try BoundsForRange optimistically for exact caret placement #218 behavior — overlay placement on those elements regresses to where it was before that PR, which was acceptable enough to ship for months.
  • Settings migration: existing installs have cotabbyFocusPollIntervalMilliseconds = 50 persisted in UserDefaults. The resolver now floors the persisted value at the shipped default, so the 50ms → 80ms bump reaches existing users. The setting stepper is hidden from the UI (SettingsView.swift:646), so the persisted value is always the previous first-launch default, never a user-chosen override — flooring is safe.
  • Follow-up worth filing: cache parameterizedAttributeNames per AX element for the duration of a focus session so we can keep the gate cheap while still picking up genuine supporters that the system later starts to advertise.

PR #218 made the BoundsForRange AX call unconditional in three places —
including the deep-tree walker that visits many AXStaticText leaves per
focus poll. BoundsForRange is a synchronous cross-process call into the
focused app's AX implementation; in Chrome that's a round-trip into the
renderer, and calling it on nodes that don't advertise support stalled
the main thread badly enough to freeze typing.

Restore the supportsBoundsForRange gate at the three call sites and keep
the rectIsNearAnchor validator for elements that do advertise support.
Bump the first-launch focus poll interval 50ms -> 80ms and floor the
persisted value at the shipped default so existing installs migrate
(the stepper is hidden from the UI, so the persisted value is always
the previous default, never a user-chosen override).
@FuJacob FuJacob merged commit 0d36bc4 into main May 25, 2026
3 checks passed
@FuJacob FuJacob deleted the hotfix/chrome-ax-hang-bounds-for-range-gate branch May 25, 2026 16:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant